package com.hackordie.commons.hardware.computer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.hackordie.commons.hardware.components.AComponent;
import com.hackordie.commons.hardware.components.NetworkCard;
import com.hackordie.commons.hardware.components.PowerSupply;
import com.hackordie.commons.hardware.components.Processor;
import com.hackordie.commons.hardware.components.RamMemory;
import com.hackordie.commons.software.AProgram;
import com.hackordie.commons.software.OperativeSistem.AOperativeSistem;
import com.hackordie.constants.EComponentsType;

public class Computer implements IComputer {

    int idComputer;

    protected List<AComponent> components;
    protected Map<EComponentsType, ActualTotalComponentsType> maxComponentes = new HashMap<EComponentsType, ActualTotalComponentsType>();
    protected AOperativeSistem operativeSystem;

    int availableEnergy;
    private int availableProcessorCapacity;

  

    public Computer(int maxFirewall, int maxProcessor, int maxNetwoekCard,
            int maxHD, int maxRAM, int maxPowerSupply) {

        maxComponentes.put(EComponentsType.Firewall,
                new ActualTotalComponentsType(0, maxFirewall));
        maxComponentes.put(EComponentsType.Processor,
                new ActualTotalComponentsType(0, maxProcessor));
        maxComponentes.put(EComponentsType.NetworkCard,
                new ActualTotalComponentsType(0, maxNetwoekCard));
        maxComponentes.put(EComponentsType.HardDisk,
                new ActualTotalComponentsType(0, maxHD));
        maxComponentes.put(EComponentsType.RamMemory,
                new ActualTotalComponentsType(0, maxRAM));
        maxComponentes.put(EComponentsType.PowerSupply,
                new ActualTotalComponentsType(0, maxPowerSupply));

        availableEnergy = 0;
        components = new ArrayList<AComponent>();
        availableProcessorCapacity = 0;
    }

    // getters and setters
    public List<AComponent> getComponentsList() {
        return components;
    }

    public AComponent getComponentByName(String name) {
        for (int i = 0; i < components.size(); i++) {
            if (components.get(i).getComponentName().equals(name)) {
                return components.get(i);
            }
        }
        return null;
    }

    public List<AComponent> getComponentByType(EComponentsType type) {

        List<AComponent> list = new ArrayList<AComponent>();

        for (int i = 0; i < components.size(); i++) {
            if (components.get(i).getComponentType().equals(type)) {
                list.add(components.get(i));
            }
        }

        return list;
    }

    public int getAvailableEnergy() {
        return availableEnergy;
    }

    public AOperativeSistem getOperativeSystem() {
        return operativeSystem;
    }

    public void setOperativeSystem(AOperativeSistem operativeSystem) {
        this.operativeSystem = operativeSystem;
    }
    
    public int getAvailableProcessorCapacity() {
        return availableProcessorCapacity;
    }

    // functions

    public boolean addComponent(AComponent component) {

        int actualComponents = maxComponentes.get(component.getComponentType())
                .getActualComponent();

        if (actualComponents < maxComponentes.get(component.getComponentType())
                .getMaxComponent()) {

            components.add(component);
            maxComponentes.get(component.getComponentType())
                    .setActualComponent((++actualComponents));
           
            if (component.getComponentType() == EComponentsType.Processor) {
                calculateTotalProcessorCapacity();
            }
            return true;
        }
        return false;
    }

    public void removeComponentByName(String name) {

        int posicion = 0;
        boolean encontrado = false;

        do {
            if (components.get(posicion).getComponentName().equals(name)) {

                //hay que remover el componente antes de recalcular la capacidad
                if (components.get(posicion).getComponentType() == EComponentsType.Processor) {
                    components.remove(posicion);
                    calculateTotalProcessorCapacity();
                } else {
                    components.remove(posicion);
                }
                encontrado = true;
            }
        } while (!encontrado && posicion < components.size());
    }

    public void removeComponentsByType(EComponentsType type) {

        for (int posicion = 0; posicion < components.size(); posicion++) {
            if (components.get(posicion).getComponentType().equals(type)) {
                components.remove(posicion);
            }
        }
    }

    public void removeAllComponents() {
        components.clear();
    }

    public void on() {

        availableEnergy = 0;

        // buscamos los componentes "PowerSupply"
        for (int i = 0; i < components.size(); i++) {

            if (components.get(i).getComponentType()
                    .equals(EComponentsType.PowerSupply)) {
                availableEnergy += ((PowerSupply) components.get(i))
                        .getGeneratedEnergy();
            }
        }

        // intento de encender todos los componentes
        for (int i = 0; i < components.size(); i++) {
            availableEnergy = components.get(i).on(availableEnergy);
        }
    }

    public void off() {
        // apagado de componentes
        for (int i = 0; i < components.size(); i++) {
            availableEnergy = components.get(i).off(availableEnergy);
        }
    }

    public String getMyIp() {

        // todas las NetworkCard tienen la misma ip, asi que devolvemos la ip de
        // la primera NetwrkCard encontrada
        List<AComponent> networkCards = getComponentByType(EComponentsType.NetworkCard);

        if (networkCards != null) {
            return ((NetworkCard) networkCards.get(0)).getIpDirection();
        }

        return null;
    }

    public List<String> getIpsConnectedTo() {

        List<String> ips = new ArrayList<String>();
        List<AComponent> networkCards = new ArrayList<AComponent>();

        networkCards = this.getComponentByType(EComponentsType.NetworkCard);

        for (int i = 0; i < networkCards.size(); i++) {
            ips.add(((NetworkCard) networkCards.get(i)).getConnectedToIp());
        }

        return ips;
    }
    
    
    public boolean executeProgram(Object[] obj) {
        
        List<AComponent> memory    = this.getComponentByType(EComponentsType.RamMemory);
        
        if( !(obj[0] instanceof AProgram) ){
            return false;
        }
        AProgram program = (AProgram) obj[0];
        
        if(!program.checkObj(obj)) {
            return false;
        }
        
        if (program.isStarted()) {
            return true;
        }
       
        if (program.getRequiredProcessor() > availableProcessorCapacity) {
            return false;
        }

        // intentamos agregar el programa a algun modulo de ram

        boolean adequatedRam = false;
        int arrayPos = 0;

        do {
            adequatedRam = ((RamMemory) memory.get(arrayPos)).setPrograms(program);
            arrayPos++;
        } while (!adequatedRam && arrayPos < memory.size());

        // si conseguimos agregar el programa a la memoria RAM, quitamos
        // capacidad al procesador
        if (adequatedRam) {
            availableProcessorCapacity -= program.getRequiredProcessor();
            
            program.setStarted(true);
        }
        
        return program.run(obj);
    }

    public void stopProgram(AProgram program) {

        List<AComponent> memory    = this.getComponentByType(EComponentsType.RamMemory);
        
        if (program.isStarted()) {

            boolean deleted = false;
            int arrayPos = 0;

            do {
                deleted = ((RamMemory) memory.get(arrayPos))
                        .deleteProgram(program.getName());
                arrayPos++;
            } while (!deleted && arrayPos < memory.size());

            // si elimino el programa de la RAM, liberamos proceso del
            // procesador
            if (deleted) {
                availableProcessorCapacity += program.getRequiredProcessor();
             
            }
        }
    }

    public boolean checkUserAndPassword(String userName, String password) {
        if (userName.equals(operativeSystem.getName())
                && password.equals(operativeSystem.getPassword())) {
            return true;
        }
        return false;
    }
    
    private void calculateTotalProcessorCapacity() {
        for(int i = 0; i < components.size(); i++ ) {
            if(components.get(i).getComponentType() == EComponentsType.Processor) {
                availableProcessorCapacity += ((Processor)components.get(i)).getTotalCapacity();
            }                    
        }
    }
}
